package com.sf.fy.uav.applet.login.controller;

import com.sf.fy.uav.applet.login.service.LoginService;
import com.sf.fy.uav.applet.utils.RandomUtils;
import com.sf.fy.uav.common.enums.ServicePrefixEnum;
import com.sf.fy.uav.common.utils.RedisPrefixKey;
import com.sf.fy.uav.common.utils.Rs;
import com.sf.fy.uav.common.utils.SfAppletParamsDecryptUtil;
import com.sf.fy.uav.sms.rest.SendSmsRest;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.Calendar;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.regex.Pattern;

/**
 * @author LTQ
 * 登录
 */
@Api(tags = "登录")
@RestController
@RequestMapping("/home/")
@Validated
public class LoginController {

    private final static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private LoginService loginService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${lock.try_time:3}")
    private long lockTryTime;

    @Autowired
    private SendSmsRest sendSmsRest;

    @Autowired
    private RedissonClient redissonClient;

    @Value("${sms.a-minute-limit: 1}")
    private Integer aMinuteLimit;

    @Value("${sms.login-minute-limit: 5}")
    private Integer loginMinuteLimit;

    @Value("${sms.half-an-hour-limit: 10}")
    private Integer halfAnHourLimit;

    @Value("${sms.a-day-limit: 20}")
    private Integer aDayLimit;

    @Value("${is-check-code:test}")
    private String isCheckCode;

    @Autowired
    private SfAppletParamsDecryptUtil sfAppletParamsDecryptUtil;

    @Operation(tags = "通过微信号登录", description = "通过微信号登录")
    @GetMapping(value = {"login-by-wechat"})
    public Rs<String> loginByWechat(@RequestParam("loginCode") String loginCode, @RequestParam("encryptedData") String encryptedData, @RequestParam("ikey") String iv, @RequestParam(value = "scene", required = false) String scene) {
        String token = loginService.loginByWechat(loginCode, encryptedData, iv, scene);
        return Rs.success(token);
    }

    @Operation(tags = "解密顺丰速运传过来的参数并注册登录", description = "解密顺丰速运传过来的参数并注册登录")
    @GetMapping(value = {"decrypt-sf-param-and-register-or-login"})
    public Rs<String> decryptSfParamAndRegisterAndLogin(@RequestParam("encryptedData") @NotBlank(message = "encryptedData不能为空") String encryptedData) {
        logger.info("encryptedData:{}", encryptedData);
        String string = sfAppletParamsDecryptUtil.decrypt(encryptedData);
        String[] strings = string.split("&");
        String mobile = "";
        String openId = "";
        for (String s : strings) {
            if (s.contains("mobile")) {
                mobile = s.split("=")[1];
            } else if (s.contains("openId")) {
                openId = s.split("=")[1];
            }
        }
        if (ObjectUtils.isEmpty(mobile)) {
            throw new RuntimeException("解密得到的参数缺少mobile");
        }
        if (ObjectUtils.isEmpty(openId)) {
            throw new RuntimeException("解密得到的参数缺少openId");
        }
        String token = loginService.loginBySfExpressData(mobile, openId);
        return Rs.success(token);
    }


    @GetMapping("get-code")
    @Operation(tags = "获取验证码", description = "获取验证码")
    public Rs<String> getCode(@RequestParam @Size(min = 11, max = 11) String phoneNumber) {
        Lock lock = null;
        try {
            lock = redissonClient.getLock(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.SEND_SMS + phoneNumber);
            boolean b = lock.tryLock(lockTryTime, TimeUnit.SECONDS);
            if (b) {
                phoneNumber = phoneNumber.trim();
                boolean is11Digits = Pattern.matches("\\d{11}", phoneNumber);
                if (!is11Digits) {
                    return Rs.error("手机号格式不正确");
                }
                Calendar calendar = Calendar.getInstance();
                calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 1);
                long aMinuteAgo = calendar.getTimeInMillis();
                Set<String> set = redisTemplate.opsForZSet().rangeByScore(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE_COUNT_DOWN + phoneNumber, aMinuteAgo, System.currentTimeMillis());
                if (set.size() >= aMinuteLimit) {
                    throw new RuntimeException("一分钟内只能获取1次");
                }
                calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 30);
                long thirtyMinuteAgo = calendar.getTimeInMillis();
                set = redisTemplate.opsForZSet().rangeByScore(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE_COUNT_DOWN + phoneNumber, thirtyMinuteAgo, System.currentTimeMillis());
                if (set.size() >= halfAnHourLimit) {
                    throw new RuntimeException("半小时内只能获取10次");
                }
                calendar.set(Calendar.MINUTE, calendar.get(Calendar.DAY_OF_MONTH) - 1);
                long aDayAgo = calendar.getTimeInMillis();
                set = redisTemplate.opsForZSet().rangeByScore(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE_COUNT_DOWN + phoneNumber, aDayAgo, System.currentTimeMillis());
                if (set.size() >= aDayLimit) {
                    throw new RuntimeException("一天内只能获取20次");
                }
                String code = RandomUtils.getStringRandom();
                sendSmsRest.sendSms(phoneNumber, code);
                redisTemplate.opsForZSet().add(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE_COUNT_DOWN + phoneNumber, code, System.currentTimeMillis());
                redisTemplate.opsForValue().set(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE + phoneNumber, code);

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (!org.springframework.util.ObjectUtils.isEmpty(lock)) {
                // 释放锁
                lock.unlock();
            }
        }
        return Rs.success();
    }

    @Operation(tags = "通过验证码登录", description = "通过验证码登录")
    @GetMapping("login-by-code")
    public Rs<String> loginByCode(@RequestParam @Size(min = 11, max = 11) @jakarta.validation.constraints.Pattern(regexp = "^1[3-9]{1}[0-9]{9}$", message = "手机号格式不正确") String phoneNumber, @RequestParam @Size(min = 6, max = 6) String code, @RequestParam(required = false) String loginCode, @RequestParam(value = "scene", required = false) String scene) {
        if (!"test".equals(isCheckCode)) {
            Calendar calendar = Calendar.getInstance();
            //五分钟内登录
            calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - loginMinuteLimit);
            Long fiveMinuteAgo = calendar.getTimeInMillis();
            Set<String> set = redisTemplate.opsForZSet().rangeByScore(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE_COUNT_DOWN + phoneNumber, fiveMinuteAgo, System.currentTimeMillis());
            if (!set.contains(code)) {
                return Rs.failed("验证失败");
            }
            String phoneCode = (String) redisTemplate.opsForValue().get(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE + phoneNumber);
            if (!code.equals(phoneCode)) {
                return Rs.failed("验证失败");
            } else {
                redisTemplate.delete(ServicePrefixEnum.SAME_CITY + RedisPrefixKey.APPLET_PHONE_CODE + phoneNumber);
            }
        } else {
            if (!code.equals("014303")) {
                return Rs.failed("验证失败");
            }
        }
        String token = loginService.loginByPhoneNumber(phoneNumber, loginCode, scene);
        return Rs.success(token);
    }
}
